#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "../include/processpool.h"

/// @brief 用户CGI请求类
class cgi_conn
{
private:
    static const int BUFFER_SIZE = 1024;
    // 内核事件表为空
    static int m_epollfd;
    int m_sockfd;
    sockaddr_in m_address;
    char m_buffer[BUFFER_SIZE];
    int m_read_idx;

public:
    cgi_conn() {}
    ~cgi_conn() {}

    /// @brief 初始化客户连接
    /// @param epollfd 内核事件表FD
    /// @param sockfd socket连接FD
    /// @param client_addr 客户端地址
    void init(int epollfd, int sockfd, const sockaddr_in &client_addr)
    {
        m_epollfd = epollfd;
        m_sockfd = sockfd;
        m_address = client_addr;
        memset(m_buffer, '\0', BUFFER_SIZE);
        m_read_idx = 0;
    }

    /// @brief 逻辑处理
    void process()
    {
        int idx = 0;
        int ret = -1;
        while (1)
        {
            idx = m_read_idx;
            ret = recv(m_sockfd, m_buffer + idx, BUFFER_SIZE - 1 - idx, 0);
            // 若读操作发生错误
            if (ret < 0)
            {
                // 暂无数据可读
                if (errno != EAGAIN)
                    removeFd(m_epollfd, m_sockfd);
                break;
            }
            // 若对方关闭连接
            else if (ret == 0)
            {
                removeFd(m_epollfd, m_sockfd);
                break;
            }
            else
            {
                m_read_idx += ret;
                printf("User Content Is :%s\n", m_buffer);
                // 若接收到\r\n字符，则开始处理客户请求
                for (; idx < m_read_idx; ++idx)
                {
                    if ((idx >= 1) && (m_buffer[idx - 1] == '\r') && (m_buffer[idx] == '\n'))
                        break;
                }
            }
            // 若未接收到\r\n字符
            if (idx == m_read_idx)
                // 继续读取客户信息
                continue;

            m_buffer[idx - 1] = '\0';
            char *file_name = m_buffer;
            // 判断客户运行的CGI程序是否存在
            if (access(file_name, F_OK) == -1)
            {
                removeFd(m_epollfd, m_sockfd);
                break;
            }
            // 创建子进程执行CGI程序
            ret = fork();
            // 出错
            if (ret == -1)
            {
                removeFd(m_epollfd, m_sockfd);
                break;
            }
            // 在父进程中
            else if (ret > 0)
            {
                removeFd(m_epollfd, m_sockfd);
                break;
            }
            // 在子进程中
            else
            {
                // 将标准输出定向至m_sockfd
                close(STDIN_FILENO);
                dup(m_sockfd);
                // 执行CGI程序
                execl(m_buffer, m_buffer, NULL);
                exit(0);
            }
        }
    }
};

// 静态成员需在类外初始化
int cgi_conn::m_epollfd = -1;

int main(int argc, const char *argv[])
{
    if (argc <= 2)
    {
        printf("Usage: %s [ip_address] [port_number]\n", argv[0]);
        return 1;
    }

    const char *IP = argv[1];
    const int PORT = atoi(argv[2]);
    int ret = 0;

    sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr(IP);
    address.sin_port = htons(PORT);

    int sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
    assert(sockfd >= 0);

    ret = bind(sockfd, (sockaddr *)&address, sizeof(address));
    assert(ret != -1);

    ret = listen(sockfd, 5);
    assert(ret != -1);

    // 创建线程池实例
    processpool<cgi_conn> *pool = processpool<cgi_conn>::create(sockfd);
    if (pool)
    {
        pool->run();
        delete pool;
    }

    // 谁创建-谁销毁
    close(sockfd);
    return 0;
}
